import React, { useState, useEffect, useCallback, useRef } from 'react';
import { useModel, useInMicroApp } from '@sui/sui';

const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay||0));

/**
 * 创建model
 * @param {*} param0 
 * @returns 
 */
export function createContextStateModel(getContextState){
  return () => {
    const isMouted = useRef(true);
    let parentContext, topContext;
    if( useInMicroApp() ){
      parentContext = (useModel('parent.context') || {})['context']
      topContext = (useModel('top.context') || {})['context']
    }
    const hasContextStateCallback = typeof getContextState === 'function';
    const [state, setState] = useState({context: {}, topContext, parentContext, loading: hasContextStateCallback, error: undefined});
    const refresh = hasContextStateCallback ? useCallback(async () => {
      isMouted && isMouted.current && setState((s) => ({ ...s, topContext, parentContext, loading: true, error: undefined }));
      try {
        const asyncFunc = () => new Promise((res) => res(getContextState(parentContext, topContext)));
        const ret = await asyncFunc();
        isMouted && isMouted.current && setState((s) => ({ ...s, topContext, parentContext, context: ret, error: undefined, loading: false }));
      } catch (e) {
        isMouted && isMouted.current && setState((s) => ({ ...s, topContext, parentContext, error: e, loading: false }));
      }
      await sleep(10)
    }, [ ]) : () => {};
   
    const setContextState = useCallback(async (newContextState) => {
      isMouted && isMouted.current && setState((s) => {
        if (typeof newContextState === 'function') {
          return { ...s, context: newContextState(s.context), topContext, parentContext, error: undefined, loading: false };
        }
        return { ...s, context: newContextState, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    const setSettingsState = useCallback(async (newSettingsState) => {
      isMouted && isMouted.current && setState((s) => {
        if (typeof newSettingsState === 'function') {
          newSettingsState = newSettingsState(s.settings || {})
        }
        return { ...s, context: {...s.context, settings: newSettingsState}, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    const setUserState = useCallback(async (newUserState) => {
      isMouted && isMouted.current && setState((s) => {
        if (typeof newUserState === 'function') {
          newUserState = newUserState(s.user || {})
        }
        return { ...s, context: {...s.context, user: newUserState}, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    const setThemeState = useCallback(async (newThemeState) => {
      isMouted && isMouted.current && setState((s) => {
        if (typeof newThemeState === 'function') {
          newThemeState = newThemeState(s.theme || {})
        }
        return { ...s, context: {...s.context, theme: newThemeState}, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    const setThemeVars = useCallback(async (newThemeVars) => {
      isMouted && isMouted.current && setState((s) => {
        const theme = Object.assign( {}, s.theme || {})
        if (typeof newThemeVars === 'function') {
          newThemeVars = newThemeVars(theme.vars || {})
        }
        theme.vars = Object.assign({}, theme.vars || {}, newThemeVars || {})
        window && typeof window.modifySuiLessVars === 'function' && window.modifySuiLessVars( theme.vars )
        return { ...s, context: {...s.context, theme}, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    const setThemeStyles = useCallback(async (newThemeStyles) => {
      isMouted && isMouted.current && setState((s) => {
        const theme = Object.assign( {}, s.theme || {})
        if (typeof newThemeStyles === 'function') {
          newThemeStyles = newThemeStyles(theme.styles || {})
        }
        theme.styles = Object.assign({}, theme.styles || {}, newThemeStyles || {})
        return { ...s, context: {...s.context, theme}, topContext, parentContext, error: undefined, loading: false };
      });
      await sleep(10)
    }, [ ]);
  
    useEffect(()=>{
      isMouted.current = true;
      refresh && refresh();
      return () => {
        isMouted.current = false
      }
    }, [])
  
    return {
      ...state,
      setContextState,
      setThemeState,
      setThemeVars,
      setThemeStyles,
      setSettingsState,
      setUserState,
      refresh
    };
  }
};

/**
 * 增加model
 * @param {*} namespace 
 * @param {*} model 
 */
export function addModel(models, namespace, model){
  models = models || {}
  namespace = model.namespace || namespace
  if(models[namespace]){
      throw new Error(`Model ${namespace} has exists. Please use other namespace.`)
  }
  models[namespace] = model;
  return models
}